package com.coder.rental.security;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.convert.NumberWithFormat;
import cn.hutool.core.date.DateTime;
import cn.hutool.jwt.JWTPayload;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.coder.rental.entity.User;
import com.coder.rental.utils.JwtUtils;
import com.coder.rental.utils.RedisUtils;
import com.coder.rental.utils.ResultCode;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * ss 认证成功后处理器
 */
@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {

    @Resource
    private RedisUtils redisUtils; // 注入组件

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, // 请求
                                        HttpServletResponse response, // 响应
                                        Authentication authentication) throws IOException, ServletException { // 认证对象
        // 1 设置客户端响应的内容类型是json格式
        response.setContentType("application/json;charset=utf-8");

        // 2 从认证信息中提取用户对象
        User user = (User)authentication.getPrincipal();

        // 3 准备token相关的数据 并生成token
        Map<String, Object> map = new HashMap<>();
        map.put("username", user.getUsername());
        map.put("userid", user.getId());
        String token = JwtUtils.createToken(map);

        // 4 解析token获取过期时间，并构建认证结果对象（在生成token时以设置了过期时间）
        NumberWithFormat claim = (NumberWithFormat) JwtUtils.parseToken(token).getClaim(JWTPayload.EXPIRES_AT);// 获取过期时间
        long expireTime = Convert.toDate(claim).getTime();
        AuthenticationResult authenticationResult = new AuthenticationResult(user.getId(), ResultCode.SUCCESS, token, expireTime);

        // 5 将认证结果转换为JSON字符串
        String result = JSON.toJSONString(authenticationResult, SerializerFeature.DisableCircularReferenceDetect); // 消除循环引用

        // 6 获取输出流 向客户端发送认证结果
        ServletOutputStream outputStream = response.getOutputStream();
        outputStream.write(result.getBytes(StandardCharsets.UTF_8));
        outputStream.flush();
        outputStream.close();

        // 7 token存入redis中，并设置其过期时间
        String tokenKey = "token:" + token; // 一定要用英文:  构造一个键（key），它的值是字符串 "token:" 和 token 变量的拼接
        long nowTime = DateTime.now().getTime();
        redisUtils.set(tokenKey, token, (expireTime - nowTime) / 1000); // set方法第三个参数是s为单位 而getTime获取到的时间都是ms为单位，因此要/1000
    }
}
